package com.fwj;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fwj.config.Constants;
import com.fwj.exception.EsbSynException;
import org.apache.log4j.Logger;

/**
 * 浙江电信ESB2.0系统和F5设备数据同步
 * @author fwj
 * @data 2015-12-6 10:51:52
 *
 */
public class SynMain_20151214 {
	
	private static Logger logger = Logger.getLogger(SynMain.class);  

	private static String data_toF5Name = "";
	private static String nl_windows = "\r\n";//windows 换行符
	private static String nl_linux = "\n";//linux 换行符
	
	public static void main(String[] args) {
		SynMain sm = new SynMain();
		try {
			//sm.esbsyn();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 程序入口
	 * @author fwj
	 * @throws EsbSynException
	 */
	public void esbsyn() throws EsbSynException{
		
		String fileContent = readToString(Constants.f5_datafile);//读取F5配置文件
		
		/*************************************
		 * 第一步
		 * 获取文件，检测文件格式和数据格式合法性
		 ************************************/
		
		//获取数据同步文件List
		List syndatalist = formatSynDataFile();
		
		List serviceCodeList = formatServiceCode(fileContent);
		
		List poolList = formatPools(fileContent);
		
		logger.info("F5配置文件service-code共计"+serviceCodeList.size()+"个");
		logger.info("F5配置文件Pools共计"+poolList.size()+"组");
		
		int all_servicecode = 0;//service-code总数
		int exist_servicecode = 0;//service-code存在数据
		int noexist_servicecode = 0;//service-code不存在数
		
		int all_pool = 0;
		int exist_pool = 0;
//		int add_pool = 0;
		
		StringBuffer sb = new StringBuffer();
		
		int cj1 = 0;
		int cj2 = 0;
		int cj3 = 0;
		int cj4 = 0;
		
		int xl = 0;
		
		for(int i=0; i<syndatalist.size();i++){
			Map map = (Map)syndatalist.get(i);
			String servicecode = (String)map.get("servicecode");
			List ipList = (List)map.get("ipList");
			
			all_servicecode = syndatalist.size();
			all_pool = syndatalist.size();
			/*************************************
			 * 检索Service-code和Pools对应关系
			 * 输出日志：Service-code共计*个，新增*个、更新*个、已存在*个；
			 ************************************/
			boolean ifservicecode = false;
			for(int j=0; j<serviceCodeList.size(); j++){
				Map smap = (Map)serviceCodeList.get(j);
				String sc = (String)smap.get("name");
				if(servicecode.equals(sc)){
					ifservicecode = true;
					exist_servicecode ++;
					break;
				}else{
					ifservicecode = false;
					continue;
				}
			}

			noexist_servicecode = all_servicecode - exist_servicecode;

			/*************************************
			 * 检索IP成员和Pools对应关系
			 * 输出日志：
			 * IP成员共计*组，已存在Pools的*组（指出对应关系）
			 * 需新增的Pools的*组（列出对应关系清单）
			 ************************************/
			boolean ipbool = false;
			String poolName = "";
			//检查pools是否存在
			for(int j=0; j<poolList.size(); j++){
				Map poolMap = (Map)poolList.get(j);
				List p_ipList = (List)poolMap.get("ip");
				if(compare(ipList, p_ipList)){
					poolName = (String)poolMap.get("pool");
					ipbool = true;
					exist_pool++;
				}
			}
//			add_pool = all_pool - exist_pool;
			/*************************************
			 * 第四步
			 * 生成F5可执行脚本（注意先后循序）
			 ************************************/
			
			String code = servicecode.split("\\.")[1];
			char[] ary = {'0','0'};
			//场景一：service-code不存在，IP字段值与Pools全匹配；1-DataGroup添加一条数据；
			if(!ifservicecode && ipbool){
				cj1++;
				sb.append("tmsh modify ltm data-group internal /Common/service-code records add { "+servicecode+" { data "+poolName+" } }"+nl_linux);
			}
			String ipstr = "";
			for(int j=0;j<ipList.size();j++){
				ipstr += (String)ipList.get(j) + " ";
			}
			//场景二：service-code不存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup添加一条数据；
			if(!ifservicecode && !ipbool){
				cj2++;
				if(sb.indexOf("add { "+ipstr+"} ") <= -1){//不存在相同的add pool ipstr
					xl++;
					String num = getNum(xl,ary);
					sb.append("tmsh create ltm pool /Common/Dubbo_"+code+"_"+num+"_pl members add { "+ipstr+"} monitor tcp_half_open"+nl_linux);
					sb.append("tmsh modify ltm data-group internal /Common/service-code records add { "+servicecode+" { data Dubbo_"+num+"_pl } }"+nl_linux);
				}else{//存在相同的add pool，截取之前的pool名称 
					
					int end = sb.indexOf("add { "+ipstr+"} ");
					String pool = sb.substring(end-29, end-9);
					sb.append("tmsh modify ltm data-group internal /Common/service-code records add { "+servicecode+" { data "+pool+" } }"+nl_linux);
				}
				
			}
			//场景三：service-code已存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup修改一条数据；
			if(ifservicecode && !ipbool){
				cj3++;
				if(sb.indexOf("add { "+ipstr+"} ") <= -1){//不存在相同的add pool ipstr
					xl++;
					String num = getNum(xl,ary);
					sb.append("tmsh create ltm pool /Common/Dubbo_"+code+"_"+num+"_pl members add { "+ipstr+"} monitor tcp_half_open"+nl_linux);
					sb.append("tmsh modify ltm data-group internal /Common/service-code records modify { "+servicecode+" { data Dubbo_"+num+"_pl } }"+nl_linux);
				}else{
					int end = sb.indexOf("add { "+ipstr+"} ");
					String pool = sb.substring(end-31, end-9);
					sb.append("tmsh modify ltm data-group internal /Common/service-code records modify { "+servicecode+" { data "+pool+" } }"+nl_linux);
				}
			}
			//场景四：service-code已存在，IP字段值与Pools全匹配；无操作；
			if(ifservicecode && ipbool){
				cj4++;
			}
		}
		
		//对sb重新排序，add pool 排列在前
		String[] sbs = null;
		if(sb.toString().indexOf(nl_linux) > -1){
			sbs = sb.toString().split(nl_linux);
		}else{
			sbs = sb.toString().split(nl_windows);
		}
		
		List first = new ArrayList();
		List second = new ArrayList();
		for(int i=0; i<sbs.length;i++){
			String str = sbs[i];
			if(str.indexOf("tmsh create ltm pool") > -1 && str.indexOf("add") > -1){
				first.add(str);
			}else{
				second.add(str);
			}
		}
		first.addAll(second);
		StringBuffer newSb = new StringBuffer();
		for(int i=0; i<first.size(); i++){
			String str = (String)first.get(i);
			newSb.append(str+nl_linux);
		}
		
		//保存配置，以防设备重启丢失
		newSb.append("tmsh save /sys config");
		
		
		//sb生成脚本文件
		writeFile(newSb.toString(),Constants.sh_data);
		
		logger.info("数据同步文件IP成员Pools共计"+all_pool+"组，已存在"+exist_pool+"组");
		logger.info("数据同步文件Service-code共计"+all_servicecode+"个，已存在"+exist_servicecode+"个，不存在"+noexist_servicecode+"个");
		
		logger.info("场景一：service-code不存在，IP字段值与Pools全匹配；1-DataGroup添加一条数据；共计： "+cj1+" 条");
		logger.info("场景二：service-code不存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup添加一条数据；共计： "+cj2+" 条");
		logger.info("场景三：service-code已存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup修改一条数据；共计： "+cj3+" 条");
		logger.info("场景四：service-code已存在，IP字段值与Pools全匹配；无操作；共计： "+cj4+" 条");
		
		
		//将处理万的文件放置历史文件夹
		move(Constants.syn_datafile + data_toF5Name, Constants.syn_datafile_his);
		
	}

	
		
	/**
	 * 第一步, 获取文件，检测文件格式和数据格式合法性
	 * @author fwj
	 * @date 2015年12月6日12:03:43
	 */
	public String getDuubToF5Data(){
		
		File file = new File(Constants.syn_datafile);
		File[] files = file.listFiles();
		List fileList = Arrays.asList(files);
		List newList = null;
		String fileContent = "";
		try {
			newList = getFileSort(fileList, Constants.syn_datafile);
			boolean boo = false;
			for(int i=0; i<newList.size(); i++){
				File nfile = (File)newList.get(i);
				if(nfile.isFile() && nfile.getName().startsWith("Dubbo_to_F5")){
					data_toF5Name = nfile.getName();
					boo = true;
					break;
				}
			}
			if(!boo){
				String msg = Constants.syn_datafile + "路径下找不到Dubbo_to_F5数据文件!";
				logger.error(msg);
				throw new EsbSynException(msg);
			}
			//Dubbo_to_F5_20151203_01.dat
			fileContent = readToString(Constants.syn_datafile + data_toF5Name).trim().replaceAll("" , "");
		} catch (EsbSynException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		return fileContent;
	}
	
	/**
	 * 格式化数据同步文件Dubbo_to_F5_XXXXXXX_01.dat，封装为LIST(需检验合法性)
	 * List<Map<[servicecode], List[ipList]>>
	 * @author fwj
	 * @date 2015年12月6日12:28:14
	 * @return
	 */
	public List<Map<String, List>> formatSynDataFile() throws EsbSynException{
		String synfile = getDuubToF5Data();
		String[] datas = null;
		if(synfile.indexOf(nl_windows) > -1){
			datas = synfile.split(nl_windows);//windows中换行符
		}else{
			datas = synfile.split(nl_linux);
		}
		
		List<Map<String, List>> list = new ArrayList<Map<String, List>>();
		//检验合法性
		logger.info("正在检验"+Constants.syn_datafile+ data_toF5Name + "文件合法性...");
		String returnMessage = "";
		for(int i=0; i<datas.length; i++){
			String ips = "";
			String data = datas[i];
			int l = i+1;
			if(data.indexOf(",")<=-1){
				returnMessage += "第"+l+"行缺少“,”分隔符！";
				continue;
			}else{
				ips = datas[i].split(",")[1];
				
				if(isIpv4(ips)){
					continue;
				}
				
				if(ips.indexOf("|")>-1){
					String ipsstr[] = ips.split("\\|");
					for(int j=0; j<ipsstr.length; j++){
						if(!isIpv4(ipsstr[j])){
							returnMessage += "第"+l+"行IP格式出错！";
							continue;
						}
					}
				}else{
					if(!isIpv4(ips)){
						returnMessage += "第"+l+"行IP格式出错！";
						continue;
					}
					returnMessage += "第"+l+"行缺少“|”分隔符！";
					continue;
				}
			}
		}
		if(!"".equals(returnMessage)){
			returnMessage += "请检查！";
			logger.info(Constants.syn_datafile + data_toF5Name + "文件格式出错，请检查！");
			logger.info(returnMessage);
			throw new EsbSynException(Constants.syn_datafile + data_toF5Name + "文件格式出错，请检查！");
		}
		try {
			for(int i=0; i<datas.length; i++){
				Map map = new HashMap();
				String data = datas[i];
				String servicecode = datas[i].split(",")[0];
				String ips = datas[i].split(",")[1];
				List ipList = new ArrayList();
				String ipsstr[] = ips.split("\\|");
				for(int j=0; j<ipsstr.length; j++){
					ipList.add(ipsstr[j]);
				}
				map.put("servicecode", servicecode);
				map.put("ipList", ipList);
				list.add(map);
				logger.info(servicecode);
			}
		}  catch (ArrayIndexOutOfBoundsException e) {
			logger.error("截取字符出错！数据同步文件"+Constants.syn_datafile+data_toF5Name+"格式不正确！" + e.getMessage());
			throw new EsbSynException(e);
		}
		return list;
	}	
	
	/**
	 * 查询F5配置中的service-code数量，并封装为List
	 * @author fwj
	 * @date 2015年12月6日12:28:14
	 * @return
	 */
	public List<Map<String, String>> formatServiceCode(String fileContent) throws EsbSynException{
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();
		try {
			//以下按固定格式写死截取字符串
			fileContent = fileContent.replaceAll(" ","");
			String[] conS = null;
			if(fileContent.indexOf(nl_windows)>-1){
				fileContent = fileContent.split("ltmdata-groupinternal/Common/service-code\\{"+nl_windows+"records")[1];
				fileContent = fileContent.split("\\}"+nl_windows+"typestring")[0];
				fileContent = fileContent.replaceAll("\\{", "").replaceAll("\\}"+nl_windows, "");
				conS = fileContent.split(nl_windows);
			}else{
				fileContent = fileContent.split("ltmdata-groupinternal/Common/service-code\\{"+nl_linux+"records")[1];
				fileContent = fileContent.split("\\}"+nl_linux+"typestring")[0];
				fileContent = fileContent.replaceAll("\\{", "").replaceAll("\\}"+nl_linux, "");
				conS = fileContent.split(nl_linux);
			}
			
			
			for(int i=1; i<conS.length; i=i+2){//从第一个开始
				Map<String, String> map =  new HashMap<String, String>();
				map.put("name", conS[i]);//service-code名称
				map.put("data",conS[i+1].split("data")[1]);//其下的pool名称
				list.add(map);
			}
		} catch (ArrayIndexOutOfBoundsException e) {
			logger.error("截取字符出错！F5配置文件格式不正确！" + e.getMessage());
			e.printStackTrace();
		}
		return list;
	}
	
	/**
	 * 查询F5配置中的Pools数量，并封装为List
	 * @author fwj
	 * @date 2015年12月6日12:28:14
	 * @return
	 */
	public List<Map<String, List>> formatPools(String fileContent) throws EsbSynException{
	
		fileContent = fileContent.replaceAll(" ","");
		
		String[] files = fileContent.split("ltmpool/Common/");
		
		List<Map<String, List>> list = new ArrayList<Map<String, List>>();
		for(int i=1; i<files.length; i++){
			if(i == files.length-1){
				files[i] = files[i].split("ltm")[0];
			}
			String pool = files[i].split("\\{")[0];
			
			String[] ips = files[i].split("Common/");
			
			List ipList = new ArrayList();
			for(int j=1; j<ips.length-1;j++){
				String ip = ips[j].split("\\{")[0];
				ipList.add(ip);
			}
			
			Map map =  new HashMap();
			map.put("pool", pool);
			map.put("ip", ipList);//包含IP的整段字符串
			list.add(map);
			
		}
		
		return list;
	}
	
    /**
     * 读取文件内容
     * @param fileName
     * @return
     * @throws EsbSynException
     */
    public static String readToString(String fileName) throws EsbSynException{
		String encoding = "UTF-8";
		File file = new File(fileName);
		if(!file.exists()){
        	//文件不存在
        	String error = "文件："+fileName + " 不存在";
        	logger.error(error);
        	throw new EsbSynException(error);
        }else{
        	Long filelength = file.length();
    		byte[] filecontent = new byte[filelength.intValue()];
    		try {
    			FileInputStream in = new FileInputStream(file);
    			in.read(filecontent);
    			in.close();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		try {
    			return new String(filecontent, encoding);
    		} catch (UnsupportedEncodingException e) {
    			System.err.println("The OS does not support " + encoding);
    			e.printStackTrace();
    			return null;
    		}finally{
    			logger.info("获取文件"+fileName+"成功!");
    		}
        }
	}
    
    /**
	 * 比较List的值
	 * @param a
	 * @param b
	 * @return
	 */
	public static <T extends Comparable<T>> boolean compare(List<T> a, List<T> b) {
        if (a.size() != b.size())
            return false;
        Collections.sort(a);
        Collections.sort(b);
        for (int i = 0; i < a.size(); i++) {
            if (!a.get(i).equals(b.get(i)))
                return false;
        }
        return true;
    }
	
	/**
	 * 获取递增序列号
	 * @param n
	 * @return
	 */
	public static String getNum(int n, char[] ary2){
		String str = String.valueOf(n);
		char[] ary1 = str.toCharArray();
		System.arraycopy(ary1, 0, ary2, ary2.length-ary1.length, ary1.length);
		String result = new String(ary2);
		Date date = new Date();
		SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
		result = sf.format(date) + "_" + result;
		return result;
	}
	
	
	/**
	 * 写入脚本文件
	 * @param str
	 */
	public void writeFile(String str, String path){
		FileOutputStream fos = null;
		String newFilename = getFileName(path, 1);
		try {
			File file = new File(path + newFilename);
			file.createNewFile();
			
			byte bytes[] = new byte[512];
			bytes = str.getBytes();   //新加的
			int b = str.length();   //改
			fos = new FileOutputStream(file);
			fos.write(bytes,0,b);
			logger.info("已生成F5可执行脚本文件："+ path + newFilename);
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(null != fos){
				try {
					fos.close();
					
				} catch (IOException e) {
					e.printStackTrace();
					logger.error(e.getMessage());
				}
			}
		}
	}
	
	/**
	 * 判断是否有重名文件,返回新建文件名
	 * @param
	 * @param n
	 * @return
	 */
	public String getFileName(String path, int n){
		char[] ary = {'0','0'};
		String filepath = path + "F5_" + getNum(n,ary) + ".sh";
		File file = new File(filepath);
		if(file.exists()){
			n++;
			return getFileName(path, n);
		}else{
			return "F5_" + getNum(n,ary) + ".sh";
		}
	}
	
	/**
	 * 判断IP格式
	 * @param ipAddress
	 * @return
	 */
	public static boolean isIpv4(String ipAddress) {  
		  
        String ip = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."  
                +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."  
                +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."  
                +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d):\\d{2,5}$";
        
        
        Pattern pattern = Pattern.compile(ip);  
        Matcher matcher = pattern.matcher(ipAddress);  
        return matcher.matches();  
  
    }  
	
	/**
	 * 获取目录下所有文件(按时间排序)
	 * 
	 * @param path
	 * @return
	 * @throws IOException 
	 */
	public static List getFileSort(List fileList, String path) throws IOException {
		if (fileList != null && fileList.size() > 0) {
			Collections.sort(fileList, new Comparator<File>() {
				public int compare(File file, File newFile) {
					//日期类型进行排序
					long filetime = file.lastModified();
					long newFiletime = newFile.lastModified();
					if (filetime < newFiletime) {
						return 1;
					} else if (filetime == newFiletime) {
						return 0;
					} else {
						return -1;
					}
				}
			});
		}
		return fileList;
	}
	
	/**
	 * 移动文件到历史文件夹
	 * @param srcFile
	 * @param destPath
	 * @return
	 */
	public static boolean move(String srcFile, String destPath){ 
		File file = new File(srcFile); 
		File dir = new File(destPath); 
		if(!dir.exists()){
			dir.mkdirs(); 
		}
		boolean success = file.renameTo(new File(dir, file.getName())); 
		return success; 
	} 

}
